package org.xiyu.yee.eggpreview.client;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.SpawnEggItem;
import net.minecraft.world.item.component.CustomData;
import org.xiyu.yee.eggpreview.Constants;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;

/**
 * 在物品栏/GUI 内将刷怪蛋显示为其实体的一个静态 3D 模型（仅客户端）。
 * 解析逻辑：
 * 1. 首先使用 SpawnEggItem 提供的默认实体类型。
 * 2. 如果存在 EntityTag.id (或 EntityTag.EntityTag 内常见结构) 则尝试替换为该自定义实体类型。
 * 3. 如果存在骑乘/Passengers 则忽略，只渲染最外层。
 */
public class SpawnEggPreviewRenderer {

    private static final Minecraft MC = Minecraft.getInstance();

    /** 缓存：相同物品 NBT（其 hash）对应已构建好的实体实例（弱引用防止内存泄漏）。 */
    private static final Map<Integer, Entity> ENTITY_CACHE = new WeakHashMap<>();
    // 限制最大递归乘骑实体数量，避免恶意 NBT 造成性能 / 递归栈问题
    private static final int MAX_PASSENGER_DEPTH = 3;
    private static final int MAX_PASSENGER_TOTAL = 6;

    /**
     * 判断是否应当覆盖渲染。
     */
    public static boolean canRender(ItemStack stack) {
        return stack.getItem() instanceof SpawnEggItem;
    }

    /**
     * 解析实体类型（考虑 NBT 自定义 id）。
     */
    public static EntityType<?> resolveEntityType(ItemStack stack) {
        if (stack == null || !(stack.getItem() instanceof SpawnEggItem egg)) return null;
        // 1. 直接获取刷怪蛋默认实体类型。SpawnEggItem#getType 需要一个 ItemStack (1.21 参数名称映射后允许 null 但底层使用 stack)，传入当前 stack。
        EntityType<?> base;
        try {
            base = egg.getType(stack); // 避免传 null 造成 NPE
        } catch (Throwable t) {
            base = null;
        }
        // 支持两种: 旧式 CUSTOM_DATA.EntityTag 以及新组件 entity_data (DataComponents.ENTITY_DATA)
        // (1) 旧路径
        CompoundTag full = stack.has(DataComponents.CUSTOM_DATA) ? stack.get(DataComponents.CUSTOM_DATA).copyTag() : null;
        if (full != null) {
            if (full.contains("EntityTag", Tag.TAG_COMPOUND)) {
                CompoundTag entityTag = full.getCompound("EntityTag");
                if (entityTag.contains("id", Tag.TAG_STRING)) {
                    String id = entityTag.getString("id");
                    EntityType<?> override = EntityType.byString(id).orElse(null);
                    if (override != null) return override;
                }
            }
            // 有些数据包可能直接写 entity_data 进 custom_data 里
            if (full.contains("entity_data", Tag.TAG_COMPOUND)) {
                CompoundTag entityTag = full.getCompound("entity_data");
                if (entityTag.contains("id", Tag.TAG_STRING)) {
                    String id = entityTag.getString("id");
                    EntityType<?> override = EntityType.byString(id).orElse(null);
                    if (override != null) return override;
                }
            }
        }
        // (2) 新组件 entity_data
        try {
            if (stack.has(DataComponents.ENTITY_DATA)) {
                CustomData cd = stack.get(DataComponents.ENTITY_DATA);
                if (cd != null) {
                    CompoundTag entityTag = cd.copyTag();
                    if (entityTag.contains("id", Tag.TAG_STRING)) {
                        String id = entityTag.getString("id");
                        EntityType<?> override = EntityType.byString(id).orElse(null);
                        if (override != null) return override;
                    }
                }
            }
        } catch (Throwable ignored) {}
        return base;
    }

    /**
     * 获取（或构建）实体实例用于静态渲染。不会加入世界。
     */
    public static Entity getOrCreateEntity(ItemStack stack, EntityType<?> type) {
        if (type == null || stack == null) return null;
        // 使用类型 + 自定义数据组件 hash 作为 key，避免 stack 直接 hash 导致频繁变化
        int nbtHash = 0;
        if (stack.has(DataComponents.CUSTOM_DATA)) {
            try { nbtHash = 31 * nbtHash + stack.get(DataComponents.CUSTOM_DATA).copyTag().hashCode(); } catch (Exception ignored) {}
        }
        // 合并 entity_data 组件 hash
        try {
            if (stack.has(DataComponents.ENTITY_DATA)) {
                CustomData cd = stack.get(DataComponents.ENTITY_DATA);
                if (cd != null) nbtHash = 31 * nbtHash + cd.copyTag().hashCode();
            }
        } catch (Throwable ignored) {}
        int key = System.identityHashCode(type) * 31 + nbtHash;
        Entity cached = ENTITY_CACHE.get(key);
        if (cached != null) return cached;
        try {
            Entity entity = type.create(MC.level);
            if (entity != null) {
                // 尝试应用部分基础 NBT (仅用于尺寸/变体) —— 非关键，可忽略异常
                CompoundTag entityTag = null;
                CompoundTag tagCustom = stack.has(DataComponents.CUSTOM_DATA) ? stack.get(DataComponents.CUSTOM_DATA).copyTag() : null;
                if (tagCustom != null) {
                    if (tagCustom.contains("EntityTag", Tag.TAG_COMPOUND)) {
                        entityTag = tagCustom.getCompound("EntityTag").copy();
                    } else if (tagCustom.contains("entity_data", Tag.TAG_COMPOUND)) {
                        entityTag = tagCustom.getCompound("entity_data").copy();
                    }
                }
                try {
                    if (entityTag == null) {
                        // 尝试新组件
                        if (stack.has(DataComponents.ENTITY_DATA)) {
                            CustomData cd = stack.get(DataComponents.ENTITY_DATA);
                            if (cd != null) entityTag = cd.copyTag();
                        }
                    }
                } catch (Throwable ignored) {}
                if (entityTag != null) {
                    try { entity.load(entityTag.copy()); } catch (Exception ignored) {}
                    if (entity instanceof LivingEntity living) {
                        applyEquipmentFromNBT(living, entityTag);
                    }
                    // 解析乘骑链
                    try {
                        buildPassengersRecursive(entity, entityTag, 0, new int[]{0});
                    } catch (Exception ignored) {}
                }
                ENTITY_CACHE.put(key, entity);
                return entity;
            }
        } catch (Exception ex) {
            Constants.LOG.debug("Failed creating preview entity: {}", ex.toString());
        }
        return null;
    }

    /**
     * 从 NBT (EntityTag) 中解析 HandItems / ArmorItems 并应用到实体。列表格式：
     * HandItems: 长度 2  (mainhand, offhand)
     * ArmorItems: 长度 4 (feet, legs, chest, head) —— 与原版序列一致。
     */
    private static void applyEquipmentFromNBT(LivingEntity living, CompoundTag entityTag) {
        if (entityTag == null) return;
        // HandItems
        try {
            if (entityTag.contains("HandItems", Tag.TAG_LIST)) {
                ListTag hands = entityTag.getList("HandItems", Tag.TAG_COMPOUND);
                for (int i = 0; i < hands.size() && i < 2; i++) {
                    Tag t = hands.get(i);
                    if (t instanceof CompoundTag ct) {
                        try {
                            ItemStack is = parseItemStackFromTag(ct);
                            if (!is.isEmpty()) {
                                living.setItemSlot(i == 0 ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, is);
                            }
                        } catch (Exception ignored) {}
                    }
                }
            }
        } catch (Exception ignored) {}

        // ArmorItems
        try {
            if (entityTag.contains("ArmorItems", Tag.TAG_LIST)) {
                ListTag armor = entityTag.getList("ArmorItems", Tag.TAG_COMPOUND);
                // 序号映射 feet, legs, chest, head
                EquipmentSlot[] order = new EquipmentSlot[]{EquipmentSlot.FEET, EquipmentSlot.LEGS, EquipmentSlot.CHEST, EquipmentSlot.HEAD};
                for (int i = 0; i < armor.size() && i < 4; i++) {
                    Tag t = armor.get(i);
                    if (t instanceof CompoundTag ct) {
                        try {
                            ItemStack is = parseItemStackFromTag(ct);
                            if (!is.isEmpty()) {
                                living.setItemSlot(order[i], is);
                            }
                        } catch (Exception ignored) {}
                    }
                }
            }
        } catch (Exception ignored) {}
        // body_armor_item 兼容：若存在且胸甲槽为空
        try {
            if (entityTag.contains("body_armor_item", Tag.TAG_COMPOUND)) {
                ItemStack body = parseItemStackFromTag(entityTag.getCompound("body_armor_item"));
                if (!body.isEmpty() && living.getItemBySlot(EquipmentSlot.CHEST).isEmpty()) {
                    living.setItemSlot(EquipmentSlot.CHEST, body);
                }
            }
        } catch (Exception ignored) {}
    }

    /**
     * 递归构造乘骑实体 (Passengers)，并调用 startRiding 形成堆叠。
     */
    private static void buildPassengersRecursive(Entity root, CompoundTag rootTag, int depth, int[] totalCounter) {
        if (depth >= MAX_PASSENGER_DEPTH) return;
        if (rootTag == null || !rootTag.contains("Passengers", Tag.TAG_LIST)) return;
        ListTag list = rootTag.getList("Passengers", Tag.TAG_COMPOUND);
        for (int i = 0; i < list.size(); i++) {
            if (totalCounter[0] >= MAX_PASSENGER_TOTAL) return;
            Tag t = list.get(i);
            if (!(t instanceof CompoundTag passTag)) continue;
            Entity passenger = createEntityFromTag(passTag);
            if (passenger == null) continue;
            totalCounter[0]++;
            passenger.startRiding(root, true);
            try { buildPassengersRecursive(passenger, passTag, depth + 1, totalCounter); } catch (Exception ignored) {}
        }
    }

    /**
     * 根据 NBT 创建单个实体 (用于 Passengers)。
     */
    private static Entity createEntityFromTag(CompoundTag tag) {
        if (tag == null) return null;
        String id = tag.getString("id");
        if (id == null || id.isEmpty()) return null;
        EntityType<?> type = EntityType.byString(id).orElse(null);
        if (type == null) return null;
        try {
            Entity e = type.create(MC.level);
            if (e != null) {
                try { e.load(tag.copy()); } catch (Exception ignored) {}
                if (e instanceof LivingEntity living) {
                    applyEquipmentFromNBT(living, tag);
                }
            }
            return e;
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 计算主实体 + 乘骑链的总高度，用于缩放。
     */
    private static float getMountedTotalHeight(Entity base) {
        if (base == null) return 0f;
        float total = base.getBbHeight();
        // Passengers 叠加（取直接骑乘链, Minecraft 渲染时沿 Y 堆叠）
        Entity current = base;
        int depth = 0;
        while (!current.getPassengers().isEmpty() && depth < MAX_PASSENGER_DEPTH) {
            Entity child = current.getPassengers().get(0); // 只取第一个主链
            total += child.getBbHeight();
            current = child;
            depth++;
        }
        return total;
    }

    /**
     * 兼容 1.21 DataComponent 变动的简单物品 NBT 解析（只读取 id 与 Count）。
     */
    private static ItemStack parseItemStackFromTag(CompoundTag tag) {
        if (tag == null) return ItemStack.EMPTY;
        if (!tag.contains("id", Tag.TAG_STRING)) return ItemStack.EMPTY;
        String id = tag.getString("id");
        ResourceLocation rl = ResourceLocation.tryParse(id);
        if (rl == null) return ItemStack.EMPTY;
        Item item = BuiltInRegistries.ITEM.getOptional(rl).orElse(null);
        if (item == null) return ItemStack.EMPTY;
        int count = 1;
        if (tag.contains("Count", Tag.TAG_ANY_NUMERIC)) {
            try { count = Math.max(1, tag.getInt("Count")); } catch (Exception ignored) {}
        }
        ItemStack stack = new ItemStack(item, count);
        // 不深入解析 components，自定义显示足够
        return stack;
    }


    /**
     * 在 GUI/物品栏中渲染实体模型，尺寸适配格子。
     * @param pose pose 堆栈
     * @param x 左上 X (像素)
     * @param y 左上 Y (像素)
     * @param stack 物品
     * @param buffer 外部 buffer 源
     * @param packedLight 光照
     * @return 是否已成功自定义渲染（调用方可据此跳过原始图标）
     */
    public static boolean renderPreview(PoseStack pose, int x, int y, ItemStack stack,
                                        MultiBufferSource buffer, int packedLight) {
        double mouseX = MC.mouseHandler.xpos() * MC.getWindow().getGuiScaledWidth() / MC.getWindow().getScreenWidth();
        double mouseY = MC.mouseHandler.ypos() * MC.getWindow().getGuiScaledHeight() / MC.getWindow().getScreenHeight();

        if (!canRender(stack) || MC.level == null) return false;
        EntityType<?> type = resolveEntityType(stack);
        Entity entity = getOrCreateEntity(stack, type);
        if (entity == null) return false;

        pose.pushPose();

        // ===== 自动缩放 =====
        float boxPixels = 140.0f;
    float h = Math.max(0.4f, getMountedTotalHeight(entity));
        float w = Math.max(0.4f, entity.getBbWidth());
        float scaleH = boxPixels / (h * 16f);
        float scaleW = boxPixels / (w * 16f);
        float scale = Math.min(scaleH, scaleW) * 1.25f; // 稍微放大


        // 平移到格子中心
        pose.translate(x + 8f, y + 12f, 150f);
        pose.scale(scale, -scale, scale); // Y 取负避免倒立

        // 鼠标相对物品格中心的偏移
        float dx = (float) (mouseX - (x + 8f));
        float dy = (float) (mouseY - (y + 8f));

// 给一个固定的“观察距离”，避免除零
        float dist = 40f;

// 构造一个“目标方向向量”
        float len = Mth.sqrt(dx * dx + dy * dy + dist * dist);

// 算 yaw（水平角度）
        float yaw = (float) Math.toDegrees(Math.atan2(dx, dist));

// 算 pitch（垂直角度，注意要反转 Y）
        float pitch = (float) Math.toDegrees(Math.asin(dy / len));

// 应用旋转
        pose.mulPose(Axis.XP.rotationDegrees(pitch));
        pose.mulPose(Axis.YP.rotationDegrees(yaw));



        // 渲染实体
        net.minecraft.client.renderer.entity.EntityRenderDispatcher dispatcher = MC.getEntityRenderDispatcher();
        dispatcher.setRenderShadow(false);

        try {
            dispatcher.render(entity, 0.0, -entity.getBbHeight() * 0.5, 0.0, 0f, 0f, pose, buffer, packedLight);
        } catch (Exception ex) {
            pose.popPose();
            return false;
        }

        pose.popPose();
        return true;
    }
    public static boolean renderPreviewItem(PoseStack pose, int x, int y, ItemStack stack,
                                        MultiBufferSource buffer, int packedLight) {
        if (!canRender(stack) || MC.level == null) return false;
        EntityType<?> type = resolveEntityType(stack);
        Entity entity = getOrCreateEntity(stack, type);
        if (entity == null) return false;

        pose.pushPose();

        // 计算缩放比例
        float targetPixels = 3f; // 目标高度
    float h = Math.max(0.4f, getMountedTotalHeight(entity));
        float base = targetPixels / (h * 16f);
        float scale = base * 2.5f;

        // 平移到物品格子中心
// 先把原点移动到格子中心
        pose.translate(x + 0F, y + 0F, 0F);

// 缩放（注意 Y 取负）
        pose.scale(scale, scale, scale);

// 旋转，先给一个 15° 俯视，再做水平旋转
        pose.mulPose(com.mojang.math.Axis.XP.rotationDegrees(15f));
        float rotY = (MC.level.getGameTime() % 360) + (System.identityHashCode(stack.getItem()) % 60);
        pose.mulPose(com.mojang.math.Axis.YP.rotationDegrees(rotY));
        net.minecraft.client.renderer.entity.EntityRenderDispatcher dispatcher = MC.getEntityRenderDispatcher();
// 渲染时稍微下移一半高度，让脚站在中心偏下
        dispatcher.render(entity, 0.0, entity.getBbHeight() * -0.5, 0.0, 0f, 0f, pose, buffer, packedLight);




        try {
            // 注意 Y 偏移用 -0.5 * 高度，把模型脚放在格子底部
            dispatcher.render(entity, 0.0, -entity.getBbHeight() * 0.5, 0.0, 0f, 0f, pose, buffer, packedLight);
        } catch (Exception ex) {
            pose.popPose();
            return false;
        }

        pose.popPose();
        return true;
    }

}
